﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.Linq;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using GBFramework;

public class BuildVersion
{
    // Use this for initialization
    public static bool bLoadAB = false;
    [MenuItem("Mytools/Packager ")]
    public static void createWindow()
    {
        //ScriptableWizard.DisplayWizard<BuildVersionWizard>("Create Version","set version");

        EditorWindow.GetWindow<BuildWindow>();
        Packager.bLoadAB = !AssetBundleManager.SimulateAssetBundleInEditor;
    }
}




public class BuildWindow : EditorWindow
{

    public string myVersion = "";

    public string outPutPath = "";


    public string[] verList = new string[2] { "内网版本", "外网版本" };

    public static int curSelect = -1;


    BuildTargetGroup transPlatform(TargetPlatform plat)
    {

        BuildTargetGroup ret = BuildTargetGroup.Standalone;

        switch (plat)
        {

            case TargetPlatform.IOS:
                ret = BuildTargetGroup.iOS;
                break;
            case TargetPlatform.Android:
                ret = BuildTargetGroup.Android;
                break;

        }
        return ret;
    }

    void SetGlobals()
    {
        Globals.dataPath = Application.dataPath;
        Globals.streamingPath = Application.streamingAssetsPath;
        Globals.persistenPath = Application.persistentDataPath;

        Globals.wwwStreamingPath = "file:///" + Application.streamingAssetsPath;
        Globals.wwwPersistenPath = "file:///" + Application.persistentDataPath;
    }

    void OnEnable()
    {
        SetGlobals();

        myVersion = VersionManager.Instance.curVersion;
    }

    void OnGUI()
    {
        SetGlobals();

        GUILayout.Label(" 指定当前版本号, 与服务器保持一致", EditorStyles.boldLabel);
        
        myVersion = GUILayout.TextField(myVersion);

        GUILayout.Space(15);

        GUILayout.Label(" 默认生成到项目目录，andord为 Proj.apk, windows 为 Proj.exe, ", EditorStyles.boldLabel);

        GUILayout.Label(" 默认生成到项目目录，IOS 为 Proj.out, ", EditorStyles.boldLabel);

        int tmp = Application.dataPath.LastIndexOf("/");

        outPutPath = Application.dataPath.Substring(0, tmp);

        if (myVersion != VersionManager.Instance.curVersion)
            VersionManager.Instance.curVersion = myVersion;

        PlayerSettings.bundleVersion = myVersion;

        PlayerSettings.productName = "五千年";

        GUILayout.Space(30);

        //=========================== 1.选择平台 ================================
        GUILayout.Label(" 选择发布平台  ", EditorStyles.boldLabel);

        GUILayout.Space(15);

        // 判断当前所在平台 
        if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
        {
            Packager.curTarget = TargetPlatform.Android;
        }
        else if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
        {
            Packager.curTarget = TargetPlatform.IOS;
        }
        else
            Packager.curTarget = TargetPlatform.Window;

        TargetPlatform select = (TargetPlatform)EditorGUILayout.EnumPopup(Packager.curTarget);

        if (select != Packager.curTarget)
        {
            // 重新判断当前版本设定
            curSelect = -1;

            //EditorUserBuildSettings.SwitchActiveBuildTarget();

            return;
        }

        Packager.curTarget = select;

        GUILayout.Space(15);

        ////=========================== 2. 清理AB文件夹  ===========================
        //if (GUILayout.Button("清理AB"))
        //{
        //    Packager.ClearABFolder();
        //}

        //GUILayout.Space(15);

        //// =========================== 3. 标记AB资源   ===========================
        //if (GUILayout.Button("标记AB"))
        //{
        //    Packager.BuildAssetMarks();
        //}

        //GUILayout.Space(15);

        if (GUILayout.Button("生成AB", GUILayout.Height(30)))
        {
            Hashtable tabAllBuildFile;

            Packager.ClearABFolder();
            tabAllBuildFile = Packager.BuildAssetMarks();
            Packager.BuildAssetResource();
            //Packager.SaveAllRoot(tabAllBuildFile);
        }

        // =========================== 4. 是否读取AB包  ===========================
        bool cur = GUILayout.Toggle(Packager.bLoadAB, "读取AB包");

        if (Packager.bLoadAB != cur)
        {
            Packager.bLoadAB = cur;
            AssetBundleManager.SimulateAssetBundleInEditor = !Packager.bLoadAB;
        }

        GUILayout.Space(30);

        GUILayout.Space(30);

        GUIContent content = new GUIContent(" 请确认完成了 AB包 的制做过程 ！！！");

        GUIStyle style = new GUIStyle();
        style.fontStyle = FontStyle.Normal;
        style.fontSize = 13;

        GUILayout.Label(content);

        GUILayout.Space(20);

        GUILayout.Label(" 外网版本将生成 IPA文件，耗时较长，请耐心等待 ........");

        GUILayout.Space(20);

        GUILayout.Label(" 选择发布版本类型:");

        GUILayout.Space(10);

        BuildTargetGroup curGroup = transPlatform(Packager.curTarget);

        string curSymbol = null;

        if (curSelect == -1)
        {

            curSymbol = PlayerSettings.GetScriptingDefineSymbolsForGroup(curGroup);

            if (curSymbol.IndexOf("RELEASE_VER", 0, curSymbol.Length) == -1)

                curSelect = 0;
            else
                curSelect = 1;
        }

        int newSelect = GUILayout.SelectionGrid(curSelect, verList, 6);

        // 处理不同版本的一些 PlayerSetting 设置
        if (newSelect != curSelect)
        {

            curSelect = newSelect;

            int offset = -1;
            switch (curSelect)
            {

                case 0:

                    curSymbol = PlayerSettings.GetScriptingDefineSymbolsForGroup(curGroup);

                    //
                    curSymbol = curSymbol.Replace(";RELEASE_VER", "");

                    offset = curSymbol.IndexOf("Log_Open", 0, curSymbol.Length);
                    if (offset == -1)
                        curSymbol = curSymbol + ";Log_Open";

                    offset = curSymbol.IndexOf("LogWarning_Open", 0, curSymbol.Length);
                    if (offset == -1)
                        curSymbol = curSymbol + ";LogWarning_Open";

                    offset = curSymbol.IndexOf("LogError_Open", 0, curSymbol.Length);
                    if (offset == -1)
                        curSymbol = curSymbol + ";LogError_Open";

                    break;
                case 1:

                    curSymbol = PlayerSettings.GetScriptingDefineSymbolsForGroup(curGroup);

                    offset = curSymbol.IndexOf("RELEASE_VER", 0, curSymbol.Length);
                    if (offset == -1)
                        curSymbol = curSymbol + ";RELEASE_VER";

                    curSymbol = curSymbol.Replace(";Log_Open", "");

                    curSymbol = curSymbol.Replace(";LogWarning_Open", "");

                    //curSymbol = curSymbol.Replace(";LogError_Open", "");

                    break;
            }

            PlayerSettings.SetScriptingDefineSymbolsForGroup(curGroup, curSymbol);

            Debug.Log(curSymbol);
        }

        GUILayout.Space(30);

        GUILayout.Space(30);


        // =========================== 5. 生成安装包    ===========================
        if (GUILayout.Button("生成安装包 ", GUILayout.Height(30)))
        {
            if (myVersion.Length == 0 || myVersion.Equals("0.0.0"))
            {

                EditorUtility.DisplayDialog(" Error ！！", " 请修改版本为有效数字", "确定");

            }
            else if (outPutPath.Length == 0)
            {

                EditorUtility.DisplayDialog(" Error ！！", " 请给定输出路径", "确定");

            }
            else
            {
                switch (Packager.curTarget)
                {
                    case TargetPlatform.IOS:
                        BuildUtil.Instance.buildIOS(outPutPath);
                        break;
                    case TargetPlatform.Window:

                        BuildUtil.Instance.buildWindows(outPutPath);
                        break;
                    case TargetPlatform.Android:
                        BuildUtil.Instance.buildAndroid(outPutPath);
                        break;
                }
            }
        };

        GUILayout.Space(30);

        if (GUILayout.Button("生成版本更新包 ", GUILayout.Height(30)))
        {
            ////清理旧AB包
            //Packager.ClearABFolder();
            ////标记AB文件
            //Packager.BuildAssetMarks();
            ////生成IOS     AB包
            //Packager.GenerateAB(BuildTarget.iOS);
            ////生成Android AB包
            //Packager.GenerateAB(BuildTarget.Android);

            PatchUtil pu = PatchUtil.Instance;
            pu.init();
            pu.buildPatch();
        }
    }
}


public class BuildUtil
{

    static BuildUtil _instance = null;

    public static BuildUtil Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new BuildUtil();
            }
            return _instance;
        }
    }

    string[] levels = { "Assets/__Scenes/launcher.unity",
                        "Assets/__Scenes/interim.unity" };

    public void buildIOS(string path)
    {

        BuildTarget type = BuildTarget.iOS;

        cleanPlugin(type);
        cleanWWise(type);
        cleanOtherAB(type);
        createVersion();

        AssetDatabase.Refresh();

        BuildPipeline.BuildPlayer(levels, path + "/proj_ios", BuildTarget.iOS, BuildOptions.Il2CPP | BuildOptions.ShowBuiltPlayer);

    }

    public void buildAndroid(string path)
    {

        BuildTarget type = BuildTarget.Android;
        cleanPlugin(type);
        cleanWWise(type);
        cleanOtherAB(type);
        createVersion();
        AssetDatabase.Refresh();

        BuildPipeline.BuildPlayer(levels, path + "/proj.apk", BuildTarget.Android, BuildOptions.ShowBuiltPlayer);

    }

    public void buildWindows(string path)
    {

        BuildTarget type = BuildTarget.StandaloneWindows;

        cleanPlugin(type);
        cleanWWise(type);
        cleanOtherAB(type);
        createVersion();
        AssetDatabase.Refresh();


        BuildPipeline.BuildPlayer(levels, path + "/proj_win/game.exe", BuildTarget.StandaloneWindows, BuildOptions.ShowBuiltPlayer | BuildOptions.Development);

    }

    public void cleanPlugin(BuildTarget os)
    {
        string parentPath = Application.dataPath + "/Plugins/";

        switch (os)
        {


            case BuildTarget.Android:

                deleteDirectroy(parentPath + "iOS");
                deleteDirectroy(parentPath + "tolua.bundle");
                deleteDirectroy(parentPath + "x86");
                deleteDirectroy(parentPath + "x86_64");

                break;
            case BuildTarget.iOS:

                deleteDirectroy(parentPath + "Android");

				deleteDirectroy(parentPath + "tolua.bundle");
                deleteDirectroy(parentPath + "x86");
                deleteDirectroy(parentPath + "x86_64");
                break;

            case BuildTarget.StandaloneWindows:
            case BuildTarget.StandaloneWindows64:
                break;
        }

    }

    public void cleanWWise(BuildTarget os)
    {

        string parentPath = Application.streamingAssetsPath + "/Audio/GeneratedSoundBanks/";


        switch (os)
        {


            case BuildTarget.Android:

                deleteDirectroy(parentPath + "iOS");

                deleteDirectroy(parentPath + "Windows");

                break;
            case BuildTarget.iOS:

                deleteDirectroy(parentPath + "Android");
                deleteDirectroy(parentPath + "Windows");
                break;

            case BuildTarget.StandaloneWindows:
            case BuildTarget.StandaloneWindows64:
                break;
        }
    }

    public void cleanOtherAB(BuildTarget os)
    {
        string parentPath = Application.streamingAssetsPath + "/AssetBundles/";
        //删除manifest 文件
        string[] manfestFiles = Directory.GetFiles(parentPath, "*.manifest", SearchOption.AllDirectories);
        string curManfestFile = Packager.GetPlatformFolderForAssetBundles(os) + ".manifest";

        for (int i = 0; i < manfestFiles.Length; i++)
        {
            string filePath = manfestFiles[i];
            if (!filePath.Contains(curManfestFile))
            {
                File.Delete(filePath);
            }

        }
        switch (os)
        {


            case BuildTarget.Android:

                deleteDirectroy(parentPath + "iOS");

                deleteDirectroy(parentPath + "Windows");

                break;
            case BuildTarget.iOS:

                deleteDirectroy(parentPath + "Android");
                deleteDirectroy(parentPath + "Windows");
                break;

            case BuildTarget.StandaloneWindows:
            case BuildTarget.StandaloneWindows64:
                break;
        }

    }

    public void getFileList()
    {



    }

    public void setVersion(float version)
    {

    }

    public void deleteDirectroy(string dirName)
    {

        DirectoryInfo d = new DirectoryInfo(dirName);

        if (d.Exists)
        {

            Directory.Delete(dirName, true);
        }
    }

    public void deleteFile(string fileName)
    {

        File.Delete(fileName);

    }

    public void copyDirectory(string fromDir, string toDir)
    {
        if (Directory.Exists(fromDir))
        {
            if (!Directory.Exists(toDir))
            {
                Directory.CreateDirectory(toDir);
            }
            string[] files = Directory.GetFiles(fromDir, "*", SearchOption.AllDirectories);
            string[] dirs = Directory.GetDirectories(fromDir, "*", SearchOption.AllDirectories);
            foreach (string soureDir in dirs)
            {
                string desDir = soureDir.Replace(fromDir, toDir);
                if (!Directory.Exists(desDir))
                {
                    Directory.CreateDirectory(desDir);
                }
            }

            foreach (string soureFile in files)
            {
                File.Copy(soureFile, soureFile.Replace(fromDir, toDir), true);
            }
        }
    }

    public void createVersion()
    {

        string streamPath = Application.streamingAssetsPath;

        FileInfo fi = new FileInfo(streamPath + "/streamPath.txt");
        using (StreamWriter sw = fi.CreateText())
        {
            getFilePath(streamPath, sw);
        }


#if USENEWASSETBUNDLESYSTEM
        string vesionDataPath = streamPath + "/VersionData.xml";
        //读取xml 如果没有则创建 第一次进行版本生成的时候不会进行versionupdate
        if ( !File.Exists(vesionDataPath) ) {
            InitVesionData(vesionDataPath,versionNum,streamPath);
            fileMessage.Clear();
        }
        else {
            VersionUpdate(vesionDataPath , versionNum,streamPath);
            fileMessage.Clear();
        }

        //写日志  创建相应的版本库
        WriteLog(vesionDataPath,versionNum);
#endif
        AssetDatabase.Refresh();
        Debug.Log("新版本生成成功,  ");
    }

    public void getFilePath(string sourcePath, StreamWriter sw)
    {

        DirectoryInfo info = new DirectoryInfo(sourcePath);

        foreach (FileSystemInfo fsi in info.GetFileSystemInfos())
        {

            if (fsi.Extension != ".meta" && fsi.Name != "streamPath.txt")
            {

                string[] r = fsi.FullName.Split(new string[] { "StreamingAssets" }, System.StringSplitOptions.None); //得到相对路径

                r[1] = r[1].Replace('\\', '/'); //安卓上只能识别"/"

                if (fsi is DirectoryInfo)
                { //是文件夹则迭代

                    sw.WriteLine(r[1] + " | 0"); //按行写入
                    bool ignored = fsi.FullName.EndsWith("AssetBundles") || fsi.FullName.EndsWith("Audio");
                    if (!ignored)
                    {
                        getFilePath(fsi.FullName, sw);
                    }

                }
                else
                {

                    sw.WriteLine(r[1] + " | 1" + "|" + string.Format("{0:F}", ((FileInfo)fsi).Length / 1024.0f)); //按行写入

                }
            }
        }
    }

    public string getVersionNum()
    {

        string streamPath = Application.streamingAssetsPath;

        byte[] ret = File.ReadAllBytes(streamPath + "/version.txt");

        if (ret == null || ret.Length == 0)
            return "1.0.0";

        string versionNum = System.Text.Encoding.Default.GetString(ret);

        return versionNum;
    }
}

//补丁包信息
public class PatchInfo
{
    public Dictionary<string, UpdateFileInfo> fileList = new Dictionary<string, UpdateFileInfo>();
    public Version ver = null;
    public bool isVaild = false;
    public string storePath = "";

    public PatchInfo(string name)
    {
        ver = new Version(name);
    }

    //从文本文件读取信息
    public void loadContent(string path)
    {
        fileList.Clear();
        string ret = null;
        path += "/files.txt";
        try
        {
            ret = File.ReadAllText(path);
            string[] fileContent = ret.Split(new string[] { "\n" }, System.StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < fileContent.Length; i++)
            {
                UpdateFileInfo fileInfo = new UpdateFileInfo(fileContent[i]);
                fileList[fileInfo.filePath] = fileInfo;
            }
            isVaild = true;
        }
        catch
        {
            Debug.Log(" 没有找到文件 files.txt");
        }
        storePath = Path.GetDirectoryName(path);
    }

    //添加文件信息
    public void addFileInfo(string content)
    {
        UpdateFileInfo info = new UpdateFileInfo(content);
        fileList[info.filePath] = info;
    }

    //获取差异文件
    public ArrayList getDiffFiles(PatchInfo other)
    {
        ArrayList list = new ArrayList();
        foreach (KeyValuePair<string, UpdateFileInfo> info in other.fileList)
        {
            string key = info.Key;
            UpdateFileInfo newInfo = info.Value;
            UpdateFileInfo curInfo = null;
            fileList.TryGetValue(key, out curInfo);
            if (curInfo != null && curInfo.equal(newInfo))
                continue;
            list.Add(key);
        }
        return list;
    }

    //获取补丁路径
    public string getPatchPath()
    {
        return storePath;
    }
}

/**
 * 补丁生成工具
 * 
 * */
public class PatchUtil
{
    static PatchUtil _instance = null;
    string sourcePath = Application.streamingAssetsPath;//源文件路径
    string targetPath = Application.dataPath + "/../../../../../patch/";//补丁存放路径
    List<PatchInfo> patchList = new List<PatchInfo>();

    public static PatchUtil Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new PatchUtil();
                _instance.init();
            }
            return _instance;
        }
    }

    public void init()
    {
        patchList.Clear();
        updatePatchList();
    }

    void updatePatchList()
    {
        string[] foldList = Directory.GetDirectories(targetPath);
        for (int i = 0; i < foldList.Length; i++)
        {
            string folderName = foldList[i];
            string vername = Path.GetFileName(folderName).Replace("_", ".");
            PatchInfo info = new PatchInfo(vername);
            info.loadContent(folderName);
            if (info.isVaild)
            {
                patchList.Add(info);
            }
        }
    }


    public void buildPatch()
    {
        //将Anroid AB 拷贝到当前项目中
        string outpath = EditorUtility.OpenFolderPanel("选择Android AB包路径", "", "");

        if (outpath == null || outpath.Length == 0) {

            EditorUtility.DisplayDialog(" Error ！！", "Android AB 包路径指定错误 ！！！", "确定");

            return;
        }


        BuildUtil.Instance.copyDirectory(outpath,
                                         Application.streamingAssetsPath + "/AssetBundles/Android");

        //1. 创建当前版本目录
        string folderName = VersionManager.Instance.curVersion.Replace(".", "_");
        folderName = targetPath + folderName;
        if (Directory.Exists(folderName)) return;

        PatchInfo patchInfo = new PatchInfo(VersionManager.Instance.curVersion);
        Directory.CreateDirectory(folderName);

        //2. 统计当前版本所有文件信息，保存至文本文件
        List<string> fileSystemEntries = new List<string>();

        fileSystemEntries.AddRange(Directory.GetFiles(sourcePath, "*", SearchOption.AllDirectories));

        FileStream fs = new FileStream(folderName + "/files.txt", FileMode.CreateNew);
        StreamWriter sw = new StreamWriter(fs);
        for (int i = 0; i < fileSystemEntries.Count; i++)
        {
            string file = fileSystemEntries[i];
            file = file.Replace("\\", "/");
            string ext = Path.GetExtension(file);

            if (file.EndsWith(".manifest")) {

                string fileName = Path.GetFileName(file);

                if ((!string.Equals(fileName, "Windows.manifest", System.StringComparison.CurrentCultureIgnoreCase))&&
                    (!string.Equals(fileName, "iOS.manifest", System.StringComparison.CurrentCultureIgnoreCase))&&
                    (!string.Equals(fileName, "Android.manifest", System.StringComparison.CurrentCultureIgnoreCase))) {

                    continue;
                }
            }

            if (file.EndsWith(".meta") || file.Contains(".DS_Store") || file.Contains("Audio/GeneratedSoundBanks")) 
                continue;

            FileStream fileStream = new FileStream(file, FileMode.Open);

            int size = (int)fileStream.Length;

            string md5 = Util.md5file(fileStream);
            string value = file.Replace(sourcePath, string.Empty).Substring(1);
            string content = value + "|" + md5 + "|" + size;
            UpdateFileInfo fileInfo = new UpdateFileInfo(content);
            patchInfo.addFileInfo(content);
            sw.WriteLine(content);
            fileStream.Close();
            Packager.UpdateProgress(i, fileSystemEntries.Count, "Generating file list..");
        }
        sw.Close(); fs.Close();

        //3.与历史版本对比压缩所有差异文件
        foreach (PatchInfo pInfo in patchList)
        {
            ArrayList diffFiles = pInfo.getDiffFiles(patchInfo);
            if (diffFiles.Count == 0) continue;

            FileStream commonStream = new FileStream(pInfo.getPatchPath() + "/Common.zip", FileMode.Create);
            ZipOutputStream commonZipper = new ZipOutputStream(commonStream);
            commonZipper.SetLevel(5);

            FileStream iosStream = new FileStream(pInfo.getPatchPath() + "/iOS.zip", FileMode.Create);
            ZipOutputStream iosZipper = new ZipOutputStream(iosStream);
            iosZipper.SetLevel(5);

            ZipOutputStream androidZipper = new ZipOutputStream(new FileStream(pInfo.getPatchPath() + "/Android.zip", FileMode.Create));
            androidZipper.SetLevel(5);
            ZipOutputStream winZipper = new ZipOutputStream(new FileStream(pInfo.getPatchPath() + "/Windows.zip", FileMode.Create));
            winZipper.SetLevel(5);

            string versionNum = pInfo.ver.curVersion;
            for (int i = 0; i < diffFiles.Count; i++)
            {
                string fileName = diffFiles[i] as string;
                ZipOutputStream compressor = commonZipper;
                if (fileName.Contains("AssetBundles/iOS/"))
                {
                    compressor = iosZipper;
                }
                else if (fileName.Contains("AssetBundles/Windows/"))
                {
                    compressor = winZipper;
                }
                else if (fileName.Contains("AssetBundles/Android/"))
                {
                    compressor = androidZipper;
                }
                compressor.PutNextEntry(new ZipEntry(fileName));
                string fullPath = sourcePath + "/" + fileName;
                Packager.UpdateProgress(i, diffFiles.Count, " Compress version: " + versionNum + " on file: " + fileName);
                byte[] data = new byte[2048];
                using (FileStream input = File.OpenRead(fullPath))
                {
                    int bytesRead;
                    while ((bytesRead = input.Read(data, 0, data.Length)) > 0)
                    {
                        compressor.Write(data, 0, bytesRead);
                    }
                }
                compressor.CloseEntry();
            }
            commonZipper.Finish();
            iosZipper.Finish();
            androidZipper.Finish();
            winZipper.Finish();
        }

        //4. 记录当前版本号
        VersionManager.Instance.saveVersion(targetPath + "version.txt");
        EditorUtility.ClearProgressBar();
    }


}